package org.redmagic.client;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.redmagic.client.api.Location;
import org.redmagic.client.api.LocationFactory;
import org.redmagic.client.utils.DocumentUtils;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;

@Component
public class RedMagicConfiguration {
	
	private static final Log LOG = LogFactory.getLog(RedMagicConfiguration.class);
	
	private ImmutableList<Location> locations = ImmutableList.of();
	
	private ImmutableMap<String, LocationFactory> locationFactories;
	
	private int index = 0;
	
	public RedMagicConfiguration(Map<String, LocationFactory> locationFactories, String configurationLocation) {
		
		this(locationFactories, configurationLocation, null);
	}
	
	public RedMagicConfiguration(Map<String, LocationFactory> locationFactories, String magicConfigFileName, List<String> magicConfigSetupFileNames) {
		
		if (magicConfigFileName == null) {
			throw new NullPointerException("Configuration location is required");
		}
		
		this.locationFactories = ImmutableMap.copyOf(locationFactories);
		
		// Initialize magic config file
		String magicConfig = RedMagicConfigInitializer.prepareMagicConfig(magicConfigFileName, magicConfigSetupFileNames);
		
		try {
			init(new ByteArrayInputStream(magicConfig.getBytes()));
		} catch (Exception e) {
			throw new RuntimeException("Unable to initialize configuration", e);
		}
	}
	
	private void init(InputStream magicConfigInputStream) throws ParserConfigurationException, SAXException, IOException {

		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		factory.setNamespaceAware(true);
		DocumentBuilder docBuilder = factory.newDocumentBuilder();
		Document configuration = docBuilder.parse(magicConfigInputStream);
		
		locations = parseLocations(configuration);
	}
	
	public Location getNextLocation() {

		if (locations.isEmpty() || index >= locations.size()) {
			return null;
		} else {
			return locations.get(index++);
		}
	}
	
	public void finished (Properties resolvedProperties) {
		
		for (Location location : locations) {
			location.finished(resolvedProperties);
		}
	}
	
	private ImmutableList<Location> parseLocations(Document configuration) {
		
		ImmutableList.Builder<Location> locations = new ImmutableList.Builder<Location>();
		
		for (Element locationElement : DocumentUtils.getChildElements(configuration.getDocumentElement())) {
			
			try {
				
				String locationFactoryName = locationElement.getNodeName() + "Factory";
				LOG.debug("Looking for factory bean: " + locationFactoryName);
				LocationFactory locationFactory = locationFactories.get(locationFactoryName);

				if (locationFactory == null) {
					throw new RuntimeException("Unable to find location factory: " + locationFactoryName);
				} else {
					locations.add(locationFactory.createLocation(locationElement));
				}
				
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		return locations.build();
	}
}


























